package com.liush.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.liush.srb.base.result.ResponseEnum;
import com.liush.srb.base.util.SrbAssert;
import com.liush.srb.core.hfb.FormHelper;
import com.liush.srb.core.hfb.HfbConst;
import com.liush.srb.core.hfb.RequestHelper;
import com.liush.srb.core.pojo.entity.TransFlow;
import com.liush.srb.core.pojo.entity.UserAccount;
import com.liush.srb.core.mapper.UserAccountMapper;
import com.liush.srb.core.pojo.entity.UserInfo;
import com.liush.srb.core.service.TransFlowService;
import com.liush.srb.core.service.UserAccountService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liush.srb.core.service.UserInfoService;
import com.liush.srb.core.util.LendNoUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 用户账户 服务实现类
 * </p>
 *
 * @author liush
 * @since 2023-03-25
 */
@Slf4j
@Service
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {

    @Resource
    UserInfoService userInfoService;
    @Resource
    TransFlowService transFlowService;
    @Resource
    UserAccountService userAccountService;

    //充值：构建自动提交的表单
    @Override
    public String charge(BigDecimal amount, String token) {
        //1、验证登录状态+验证用户账户状态(锁定 绑定hfb..)
        UserInfo userInfo = userInfoService.checkUserStatus(token);
        //2、校验充值金额
        SrbAssert.assertTrue(amount.doubleValue() > 0, ResponseEnum.NOT_CHARGE_ERROR);
        //3、准备表单参数
        Map<String, Object> params = new HashMap<>();
        params.put("agentId", HfbConst.AGENT_ID);//商户id
        String chargeNo = LendNoUtils.getChargeNo();
        params.put("agentBillNo", chargeNo);//商户充值单号：必须唯一  对账
        params.put("bindCode", userInfo.getBindCode());//会员在hfb开通的账户的唯一编号
        params.put("chargeAmt", amount);//充值金额  用户充值提现不扣除手续费：借款人借款成功扣除一部分、投资人回款时扣除一部分手续费
        params.put("feeAmt", "0");//手续费
        //http://192.168.1.171/api/core/userAccount/notify
        params.put("notifyUrl", HfbConst.RECHARGE_NOTIFY_URL);//hfb处理用户充值成功后的回调接口，地址必须改为 nginx服务器的地址
        params.put("returnUrl", HfbConst.RECHARGE_RETURN_URL);//hfb处理用户充值成功同步给用户响应的地址
        params.put("timestamp", System.currentTimeMillis());//时间戳
        params.put("sign", RequestHelper.getSign(params));//以上数据生成的签名  加密字符串

        //保存交易日志
        TransFlow transflow = new TransFlow();
        transflow.setUserId(userInfo.getId());
        transflow.setUserName(userInfo.getName());
        transflow.setTransNo(chargeNo);
        transflow.setTransType(1);//1：充值
        transflow.setTransTypeName("充值");
        transflow.setTransAmount(amount);
        transFlowService.save(transflow);

        //参数1：汇付宝充值接口地址   参数2：该接口需要的参数
        return FormHelper.buildForm(HfbConst.RECHARGE_URL, params);
    }

    //保证幂等性：保证携带了相同唯一token的请求 业务方法只处理一次即可
    // 数据库：  第一步生成充值表单时将充值日志存到数据库日志表中(transflow表) 状态设置为0(交易未完成)
    //           第二步回调时 如果transflow表中存在对应的日志且状态为0表示第一次回调，执行业务代码并更新日志状态为1
    @Override
    public String rechargeNotify(Map<String, Object> map) {
        //1、验签
        boolean signEquals = RequestHelper.isSignEquals(map);
        //以前自己的业务抛出异常 由全局异常处理器处理返回一个失败的ResponseVo给前端
        //当前接口由hfb来调用，抛出异常由全局异常处理器返回一个失败的ResponseVo给hfb
//        SrbAssert.assertTrue(signEquals,ResponseEnum.WX_LOGIN_CALLBACK_TOKEN_ERROR);
        if (!signEquals) {
            log.error("充值回调失败,签名错误：{}", JSON.toJSONString(map));
            return "fail";
        }
        //2、验证hfb处理业务的状态
        String resultCode = map.get("resultCode").toString();//0001代表成功
        String resultMsg = map.get("resultMsg").toString();//状态码描述
        String agentBillNo = map.get("agentBillNo").toString();//上一步生成的商户的订单号
        String bindCode = map.get("bindCode").toString();//充值的会员的hfb账号
        String chargeAmt = map.get("chargeAmt").toString();//充值金额
        String hyFee = map.get("hyFee").toString();//hfb收取商家的手续费
        String mchFee = map.get("mchFee").toString();//商户扣除的用户的手续费
//        SrbAssert.assertTrue("0001".equals(resultCode),ResponseEnum.WEIXIN_CALLBACK_PARAM_ERROR);
        if (!"0001".equals(resultCode)) {
            log.error("充值回调失败,状态码错误: {}", JSON.toJSONString(map));
            return "fail";
        }
        //3、验证充值金额+ 幂等性验证
        //3.1 幂等性验证
        //根据agentBillNo查询交易日志
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class)
                .eq(TransFlow::getTransNo, agentBillNo));
        if (transFlow == null) {
            //交易号不存在
            log.error("充值回调失败,交易编号错误: {}", JSON.toJSONString(map));
            return "fail";
        }
        if (transFlow.getStatus() == 1) {
            //交易号存在 并且已完成
            return "success";
        }
        //交易号存在 并且交易未完成
        //3.2 校验充值金额
        if (transFlow.getTransAmount().compareTo(new BigDecimal(chargeAmt)) != 0) {
            log.error("充值回调失败,hfb充值金额错误: {}", JSON.toJSONString(map));
            return "fail";
        }
        //4、更新用户账户余额
        //bindCode在userInfo表中对应唯一的一个用户
        //userAccount表没有存bindCode，绑定的是userId
        // 根据bindCode查询userId，根据userId更新userAccount账户金额在之前基础上+本次充值金额
        userAccountService.updateAmountByBindCode(new BigDecimal(chargeAmt), bindCode);
        //5、更新交易日志
        transFlow.setStatus(1);
        transFlow.setCreateTime(null);
        transFlow.setUpdateTime(null); //修改update的值为null  触发自动填充
        transFlow.setMemo(JSON.toJSONString(map));
        transFlowService.updateById(transFlow);

        //返回成功
        return "success";
    }

    @Override
    public boolean updateAmountByBindCode(BigDecimal amount, String bindCode) {
        return baseMapper.updateAmountByBindCode(amount, bindCode);
    }

    @Override
    public UserAccount getUserAccountByUserId(Long userId) {
        return this.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, userId));
    }

    @Override
    public boolean updateInvestAmountByUserId(Long userId, String voteAmt) {
        return baseMapper.updateInvestAmountByUserId(userId, voteAmt);
    }

    @Override
    public boolean updateFreezeAmountByUserId(Long investUserId, BigDecimal investAmount) {
        return baseMapper.updateFreezeAmountByUserId(investUserId, investAmount);
    }
}
